package com.glodon.paas.job.model;

import static com.glodon.paas.job.JobConstants.*;
import static com.glodon.paas.job.model.TaskStatus.*;

import java.sql.Timestamp;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Transient;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.glodon.paas.job.JobConstants;
import com.glodon.paas.job.exception.JsonException;
import com.glodon.paas.job.exception.TaskConvertException;
import com.glodon.paas.job.util.JobUtil;
import com.glodon.paas.job.util.JsonUtil;

@Entity
@Table(name = "task")
public class Task {
    private String taskId;
    private String taskType;
    private Timestamp createTime;
    private Timestamp startTime;
    private Timestamp endTime;
    private Timestamp jobServerSumbitTime;
    private long start;
    private long outTime;
    private transient Map values = new HashMap();
    private TaskStatus status;
    private String originFilename;
    
    public static String generateId() {
        UUID uuid = UUID.randomUUID();
        return uuid.toString().replaceAll("-", "");
    }

    public static Task createTask(String type) {
        return createTask(type, null);
    }

    public static Task createTask(String type, Map values) {
        String id = Task.generateId();
        Task task = new Task();
        
        task.setJobServerSumbitTime(new Timestamp(System.currentTimeMillis()));
        task.setTaskId(id);
        task.setTaskType(type);
        task.setStatus(TaskStatus.created);
        if (values != null) {
            task.values = values;
        }
        return task;
    }

    public static Task createTask(Message msg) throws TaskConvertException {
        try {
            if (msg instanceof TextMessage) {
                TextMessage textMessage = (TextMessage) msg;
                String json = textMessage.getText();
                Task task = JsonUtil.jsonToObject(json, Task.class);
                task.start = System.currentTimeMillis();
                task.outTime = JobUtil.getMessageOutTime(msg).getTime();
                return task;
            } else {
                throw new TaskConvertException("can't convert: [" + msg + "] to task");
            }
        } catch (Exception e) {
            throw new TaskConvertException("can't convert: [" + msg + "] to task", e);
        }
    }

    public void setTaskId(String id) {
        this.taskId = id;
    }

    @Id
    @Column(name = "id")
    public String getTaskId() {
        return taskId;
    }

    public void setTaskInfo(String json) throws JsonException {
        values = JsonUtil.jsonToMap(json);
    }

    @Column(columnDefinition = "text")
    @JsonIgnore
    public String getTaskInfo() {
        return JsonUtil.mapToJson(values);
    }

    public Object getValue(String key) {
        return values.get(key);
    }

    @Transient
    public Map getValues() {
        return values;
    }

    public void addValue(String key, Object value) {
        values.put(key, value);
    }

    public void addValues(Map newValues) {
        values.putAll(newValues);
    }

    public void setCreateTime(Timestamp createTime) {
        this.createTime = createTime;
    }

    @Column(name = "create_time")
    public Timestamp getCreateTime() {
        return createTime;
    }

    @Column(name = "jobserver_sumbitTime")
    public Timestamp getJobServerSumbitTime() {
		return jobServerSumbitTime;
	}

	public void setJobServerSumbitTime(Timestamp jobServerSumbitTime) {
		this.jobServerSumbitTime = jobServerSumbitTime;
	}

	public Timestamp getStartTime() {
        return startTime;
    }

    public void setStartTime(Timestamp startTime) {
        this.startTime = startTime;
    }

    public Timestamp getEndTime() {
        return endTime;
    }

    public void setEndTime(Timestamp endTime) {
        this.endTime = endTime;
    }

    @JsonIgnore
    @Transient
    public Date getNow() {
        if (outTime != 0 && start != 0)
            return new Date(outTime + System.currentTimeMillis() - start);
        else
            return null;
    }

    public String getTaskType() {
        return taskType;
    }

    public void setTaskType(String taskType) {
        this.taskType = taskType;
    }

    @Enumerated(EnumType.STRING)
    public TaskStatus getStatus() {
        return status;
    }

    public void setStatus(TaskStatus status) {
        this.status = status;
        if (status == failure)
            addFailCount();
    }

    private void addFailCount() {
        Object value = getValue(FAIL_COUNT);
        Integer failCount = value == null ? 0 : (Integer) value;
        addValue(FAIL_COUNT, failCount + 1);
    }

    @JsonIgnore
    @Transient
    public int getFailCount() {
        Object value = getValue(FAIL_COUNT);
        Integer failCount = value == null ? 0 : (Integer) value;
        return failCount;
    }

    @Override
    public String toString() {
        return toJson();
    }

    public String toJson() {
        return JsonUtil.objectToJson(this);
    }

    @Column(name = "originFilename")
    public String getOriginFilename() {
		return originFilename;
	}

	public void setOriginFilename(String originFilename) {
		this.originFilename = originFilename;
	}

	public Message toMessage(Session session) throws JMSException {
        TextMessage message = session.createTextMessage(toJson());
        // property id for cancel task, property type for console concurrent
        message.setStringProperty(JobConstants.PROPERTY_TASK_ID, taskId);
        message.setStringProperty(JobConstants.PROPERTY_TASK_TYPE, taskType);
        return message;
    }
}
